home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Tool Chest / Text / WASTE / WASTE 1.2a2 / WEHighLevelEditing.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-10-12  |  31.7 KB  |  1,319 lines  |  [TEXT/CWIE]

  1. /*
  2.  *    WEHighLevelEditing.c
  3.  *
  4.  *    WASTE PROJECT
  5.  *  High-Level Editing Routines
  6.  *
  7.  *  Copyright (c) 1993-1995 Marco Piovanelli
  8.  *    All Rights Reserved
  9.  *
  10.  *  C port by Dan Crevier
  11.  *
  12.  */
  13.  
  14.  
  15. #include "WASTEIntf.h"
  16.  
  17. typedef struct DoubleByte {
  18.     char firstByte;
  19.     char secondByte;
  20. } DoubleByte;
  21.  
  22. pascal WEActionHandle WEGetActionStack(WEHandle hWE)
  23. {
  24.     return (*hWE)->hActionStack;
  25. } // WEGetActionStack
  26.  
  27. pascal OSErr WEPushAction(WEActionHandle hAction)
  28. {
  29.     WEPtr pWE;
  30.     WEActionHandle hLast;
  31.  
  32.     // find the last action in the given stack
  33.     hLast = hAction;
  34.     while ((*hLast)->hNext != NULL)
  35.         hLast = (*hLast)->hNext;
  36.  
  37.     // prepend hAction in front of the action stack
  38.     pWE = *((*hAction)->hOwner);
  39.     (*hLast)->hNext = pWE->hActionStack;
  40.     pWE->hActionStack = hAction;
  41.  
  42.     return noErr;
  43. } // WEPushAction
  44.  
  45. pascal OSErr WENewAction(long rangeStart, long rangeEnd, long newTextLength,
  46.                             WEActionKind actionKind, WEActionFlags actionFlags,
  47.                             WEHandle hWE, WEActionHandle *hAction)
  48. {
  49.     WEActionPtr pAction;
  50.     OSErr err;
  51.  
  52.     // allocate a new action record
  53.     if ((err = _WEAllocate(sizeof(WEAction), kAllocClear, (Handle *)hAction)) != noErr) 
  54.         goto cleanup1;
  55.  
  56.     // lock it down
  57.     HLock((Handle)*hAction);
  58.     pAction = **hAction;
  59.  
  60.     // fill in the fields
  61.     pAction->hOwner = hWE;
  62.     pAction->delRangeStart = rangeStart;
  63.     pAction->delRangeLength = newTextLength;
  64.     pAction->insRangeLength = rangeEnd - rangeStart;
  65.     pAction->actionKind = actionKind;
  66.     pAction->actionFlags = actionFlags;
  67.  
  68.     // remember selection range
  69.     WEGetSelection(&pAction->hiliteStart, &pAction->hiliteEnd, hWE);
  70.  
  71.     // allocate a handle to hold the text to be saved, unless otherwise specified
  72.     if ((actionFlags & weAFDontSaveText) == 0) 
  73.     {
  74.         if ((err = _WEAllocate(0, kAllocTemp, &pAction->hText)) != noErr) 
  75.             goto cleanup1;
  76.     }
  77.  
  78.     // allocate a handle to hold the styles to be saved, unless otherwise specified
  79.     if ((actionFlags & weAFDontSaveStyles) == 0) 
  80.     {
  81.         if ((err = _WEAllocate(0, kAllocTemp, &pAction->hStyles)) != noErr) 
  82.             goto cleanup1;
  83.     }
  84.  
  85. #if WASTE_OBJECTS
  86.     // allocate a handle to hold the "soup" to be saved, unless otherwise specified
  87.     if ((actionFlags & weAFDontSaveSoup) == 0) 
  88.     {
  89.         if ((err = _WEAllocate(0, kAllocTemp, &pAction->hSoup)) != noErr) 
  90.             goto cleanup1;
  91.     }
  92. #endif
  93.     
  94.     // make a copy of text range
  95.     err = WECopyRange(rangeStart, rangeEnd, pAction->hText, (Handle) pAction->hStyles,
  96.                     pAction->hSoup, hWE);
  97.     if (err != noErr)
  98.         goto cleanup1;
  99.  
  100.     // unlock action record
  101.     HUnlock((Handle)*hAction);
  102.  
  103.     // skip clean-up section
  104.     goto cleanup0;
  105.  
  106. cleanup1:
  107.     // clean up
  108.     _WEForgetHandle(&pAction->hText);
  109.     _WEForgetHandle(&pAction->hStyles);
  110. #if WASTE_OBJECTS
  111.     _WEForgetHandle(&pAction->hSoup);
  112. #endif
  113.     _WEForgetHandle((Handle *)hAction);
  114.  
  115. cleanup0:
  116.     // return result code
  117.     return err;
  118.  
  119. } // WENewAction
  120.  
  121. pascal void WEDisposeAction(WEActionHandle hAction)
  122. {
  123.     WEActionPtr pAction;
  124.     WEActionHandle hNext;
  125.  
  126.     while (hAction != NULL)
  127.     {
  128.         // lock the action record
  129.         HLock((Handle)hAction);
  130.         pAction = *hAction;
  131.         hNext = pAction->hNext;
  132.  
  133.         // throw away text, styles and soup
  134.         _WEForgetHandle(&pAction->hText);
  135.         _WEForgetHandle(&pAction->hStyles);
  136. #if WASTE_OBJECTS
  137.         _WEForgetHandle(&pAction->hSoup);
  138. #endif
  139.  
  140.         // throw away the action record itself
  141.         DisposeHandle((Handle)hAction);
  142.  
  143.         // repeat the same sequence with all linked actions
  144.         hAction = hNext;
  145.  
  146.     } // while
  147. } // WEDisposeAction
  148.  
  149. pascal void WEForgetAction(WEActionHandle *hAction)
  150. {
  151.     WEActionHandle theAction;
  152.  
  153.     theAction = *hAction;
  154.     if (theAction != NULL) 
  155.     {
  156.         *hAction = NULL;
  157.         WEDisposeAction(theAction);
  158.     }
  159. } // WEForgetAction
  160.  
  161. pascal OSErr WEDoAction(WEActionHandle hAction)
  162. {
  163.     WEActionHandle hRedoAction;
  164.     WEActionPtr pAction;
  165.     WEHandle hWE;
  166.     WEPtr pWE;
  167.     long offset, delOffset, insOffset;
  168.     long redrawStart, redrawEnd;
  169.     Boolean saveActionLock, saveWELock, saveTextLock;
  170.     OSErr err;
  171.  
  172.     // sanity check: make sure hAction isn't NULL
  173.     if (hAction == NULL) 
  174.     {
  175.         return nilHandleErr;
  176.     }
  177.     
  178.     // get handle to associated WE instance
  179.     hWE = (*hAction)->hOwner;
  180.  
  181.     // lock the WE record
  182.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  183.     pWE = *hWE;
  184.  
  185.     // return an error code if this instance is read-only
  186.     err = weReadOnlyErr;
  187.     if (BTST(pWE->features, weFReadOnly))
  188.         goto cleanup;
  189.  
  190.     // stop any ongoing inline input session
  191.     WEStopInlineSession(hWE);
  192.  
  193.     // hide selection highlighting and the caret
  194.     _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  195.     if (BTST(pWE->flags, weFCaretVisible))
  196.         _WEBlinkCaret(hWE);
  197.  
  198.     redrawStart = LONG_MAX;
  199.     redrawEnd = 0;
  200.  
  201.     do
  202.     {
  203.  
  204.         // lock the action record
  205.         saveActionLock = _WESetHandleLock((Handle)hAction, true);
  206.         pAction = *hAction;
  207.         offset = pAction->delRangeStart;
  208.         delOffset = offset + pAction->delRangeLength;
  209.         insOffset = offset + pAction->insRangeLength;
  210.  
  211.         // if undo support is enabled, save the range to be affected by this action
  212.         if (BTST(pWE->features, weFUndoSupport)) 
  213.         {
  214.             if (WENewAction(offset, delOffset, pAction->insRangeLength, pAction->actionKind,
  215.                 (pAction->actionFlags ^ weAFIsRedo), hWE, &hRedoAction) == noErr) 
  216.             {
  217.                 if (WEPushAction(hRedoAction) != noErr)
  218.                 {
  219.                     ;
  220.                 }
  221.             }
  222.         }
  223.         if (pAction->hText != NULL) 
  224.         {
  225.  
  226.             // delete the range to replace
  227.             if ((err = _WEDeleteRange(offset, delOffset, hWE)) != noErr) 
  228.                 goto cleanup;
  229.  
  230.             // insert the saved text
  231.             saveTextLock = _WESetHandleLock(pAction->hText, true);
  232.             err = _WEInsertText(offset, *pAction->hText, pAction->insRangeLength, hWE);
  233.             _WESetHandleLock(pAction->hText, saveTextLock);
  234.             if (err != noErr) 
  235.                 goto cleanup;
  236.         }
  237.  
  238.         // apply the saved styles, if any
  239.         if (pAction->hStyles != NULL) 
  240.         {
  241.             if ((err = _WEApplyStyleScrap(offset, insOffset, (StScrpHandle)pAction->hStyles, hWE)) != noErr) 
  242.                 goto cleanup;
  243.         }
  244.  
  245. #if WASTE_OBJECTS
  246.         // the same goes for the soup
  247.         if (pAction->hSoup != NULL) 
  248.         {
  249.             if ((err = _WEApplySoup(offset, pAction->hSoup, hWE)) != noErr) 
  250.                 goto cleanup;
  251.         }
  252. #endif
  253.  
  254.         // adjust redraw range
  255.         if (offset < redrawStart) 
  256.             redrawStart = offset;
  257.         if (insOffset > redrawEnd) 
  258.             redrawEnd = insOffset;
  259.  
  260.         // unlock action record
  261.         _WESetHandleLock((Handle)hAction, saveActionLock);
  262.  
  263.         // go to next action
  264.         hAction = (*hAction)->hNext;
  265.     } while (hAction != NULL);
  266.  
  267.     // restore the original selection range
  268.     pWE->selStart = pAction->hiliteStart;
  269.     pWE->selEnd = pAction->hiliteEnd;
  270.  
  271.     // redraw the text
  272.     if ((err = _WERedraw(redrawStart, redrawEnd, hWE)) != noErr) 
  273.         goto cleanup;
  274.  
  275.     // clear result code
  276.     err = noErr;
  277.  
  278. cleanup:
  279.     // unlock the WE record
  280.     _WESetHandleLock((Handle) hWE, saveWELock);
  281.  
  282.     // return result code
  283.     return err;
  284. } // WEDoAction
  285.  
  286. pascal OSErr WEUndo(WEHandle hWE)
  287. {
  288.     WEPtr pWE;
  289.     WEActionHandle hAction;
  290.     Boolean saveWELock;
  291.     OSErr retval;
  292.  
  293.     // lock the WE record
  294.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  295.     pWE = *hWE;
  296.     
  297.     // "detach" the action stack from the WE instance
  298.     hAction = pWE->hActionStack;
  299.     pWE->hActionStack = NULL;
  300.  
  301.     if (hAction != NULL)
  302.     {
  303.  
  304.         // undoing a change _decrements_ the modification count;
  305.         // redoing the change increments it again
  306.         if (((*hAction)->actionFlags & weAFIsRedo) != 0)
  307.             pWE->modCount++;
  308.         else
  309.             pWE->modCount--;
  310.  
  311.         // perform the action...
  312.         retval = WEDoAction(hAction);
  313.  
  314.         // ...and throw it away
  315.         WEDisposeAction(hAction);
  316.     }
  317.     else
  318.     {
  319.         // return an error code if the undo buffer is empty
  320.         retval = weCantUndoErr;
  321.     }
  322.  
  323.     // unlock the WE record
  324.     _WESetHandleLock((Handle) hWE, saveWELock);
  325.  
  326.     return retval;
  327. }
  328.  
  329. pascal void WEClearUndo(WEHandle hWE)
  330. {
  331.     // dispose of the action chain associated with the given WE instance
  332.     WEForgetAction(&(*hWE)->hActionStack);
  333. } // WEClearUndo
  334.  
  335. pascal WEActionKind WEGetUndoInfo(Boolean *redoFlag, WEHandle hWE)
  336. {
  337.     WEActionHandle hAction;
  338.     WEActionKind theKind = weAKNone;
  339.     Boolean theFlag = false;
  340.     
  341.     if ((hAction = (*hWE)->hActionStack) != NULL)
  342.     {
  343.         theKind = (*hAction)->actionKind;
  344.         theFlag = (((*hAction)->actionFlags & weAFIsRedo) != 0);
  345.     }
  346.     
  347.     if (redoFlag != NULL)
  348.         *redoFlag = theFlag;
  349.     return theKind;
  350. } // WEGetUndoInfo
  351.  
  352. pascal unsigned long WEGetModCount(WEHandle hWE)
  353. {
  354.     return (*hWE)->modCount;
  355. } // WEGetModCount
  356.  
  357. pascal void WEResetModCount(WEHandle hWE)
  358. {
  359.     (*hWE)->modCount = 0;
  360.     WEClearUndo(hWE);
  361. } // WEResetModCount
  362.  
  363. pascal void _WEAdjustUndoRange(long moreBytes, WEHandle hWE)
  364. {
  365.     WEActionHandle hAction;
  366.  
  367.     if ((hAction = (*hWE)->hActionStack) != NULL) 
  368.         (*hAction)->delRangeLength += moreBytes;
  369.  
  370. } // _WEAdjustUndoRange
  371.  
  372. pascal OSErr _WETypeChar(char theByte, WEHandle hWE)
  373. {
  374.     WEPtr pWE;
  375.     DoubleByte db;
  376.     long offset, endOffset, charLength;
  377.     OSErr err;
  378.  
  379.     pWE = *hWE;                    // the WE record must be already locked
  380.     charLength = 1;                // assume 1-byte character by default
  381.     db.firstByte = theByte;
  382.     offset = pWE->selStart;
  383.  
  384.     // delete current selection, if any
  385.     if ((err = _WEDeleteRange(offset, pWE->selEnd, hWE)) != noErr) 
  386.         goto cleanup2;
  387.     
  388.     pWE->selEnd = offset; // needed in case we take a premature exit
  389.  
  390.     // make sure the font script is synchronized with the keyboard script
  391.     _WESynchNullStyle(hWE);
  392.  
  393.     if (BTST(pWE->flags, weFDoubleByte))
  394.     {
  395.  
  396.         // special processing for double-byte characters
  397.         if (pWE->firstByte != 0) 
  398.         {
  399.  
  400.             // if this byte is the second half of a double-byte character,
  401.             // insert the two bytes at the same time (flush the double-byte cache)
  402.             db.firstByte = pWE->firstByte;
  403.             db.secondByte = theByte;
  404.             charLength = 2;
  405.             pWE->firstByte = 0;
  406.         }
  407.         else
  408.         {
  409.  
  410.             // if theByte is the first half of a double-byte character, just cache it and exit
  411.             if (CallWECharByteProc(&theByte, 0, FontToScript(pWE->nullStyle.runStyle.tsFont),
  412.                 hWE, pWE->charByteHook) == smFirstByte)
  413.             {
  414.                 pWE->firstByte = theByte;
  415.                 return noErr;
  416.             }
  417.         }
  418.  
  419.     } // if double-byte script installed
  420.  
  421.     // insert the new character into the text
  422.     if ((err = _WEInsertText(offset, (Ptr)&db, charLength, hWE)) != noErr) 
  423.         goto cleanup2;
  424.  
  425.     // adjust undo buffer for the new character
  426.     _WEAdjustUndoRange(charLength, hWE);
  427.  
  428.     // invalid the null style
  429.     BCLR(pWE->flags, weFUseNullStyle);
  430.  
  431.     // move the insertion point after the new character
  432.     endOffset = offset + charLength;
  433.     pWE->selStart = endOffset;
  434.     pWE->selEnd = endOffset;
  435.  
  436.     // redraw the text
  437.     if ((err = _WERedraw(offset, endOffset, hWE)) != noErr) 
  438.         goto cleanup2;
  439.  
  440. cleanup1:
  441.     // clear result code
  442.     err = noErr;
  443.  
  444. cleanup2:
  445.     // return result code
  446.     return err;
  447.  
  448. } // _WETypeChar
  449.  
  450. pascal OSErr _WEBackspace(WEHandle hWE)
  451. {
  452.     // this routine is called by WEKey to handle the backspace key
  453.  
  454.     WEPtr pWE = *hWE;    // assume WE record is already locked
  455.     WEActionPtr pAction;
  456.     long rangeStart, rangeEnd, charLength;
  457.     WERunInfo runInfo;
  458.     DoubleByte db;
  459.     Boolean saveActionLock;
  460.     OSErr err;
  461.  
  462.     // calculate the text range to delete
  463.     // if the selection is non-empty, delete that
  464.     rangeStart = pWE->selStart;
  465.     rangeEnd = pWE->selEnd;
  466.     if (rangeStart == rangeEnd) 
  467.     {
  468.  
  469.         // otherwise the selection is an insertion point
  470.         // do nothing if insertion point is at the beginning of the text
  471.         if (rangeStart == 0)
  472.             return noErr; 
  473.  
  474.         // determine the byte-type of the character preceding the insertion point
  475.         charLength = (WECharByte(rangeStart - 1, hWE) == smSingleByte) ? 1 : 2;
  476.         rangeStart -= charLength;
  477.  
  478.         if (pWE->hActionStack != NULL) 
  479.         {
  480.             // UNDO SUPPORT FOR BACKSPACES
  481.  
  482.             // lock the action record
  483.             saveActionLock = _WESetHandleLock((Handle) pWE->hActionStack, true);
  484.             pAction = *pWE->hActionStack;
  485.  
  486.             // backspaces over the newly entered text aren't a problem
  487.             if (pAction->delRangeLength > 0) 
  488.                 pAction->delRangeLength -= charLength;
  489.             else
  490.             {
  491.  
  492.                 // the hard part comes when backspacing past the new text because
  493.                 // the user is about to delete a character not included in the block we saved
  494.                 db.firstByte = WEGetChar(rangeStart, hWE);
  495.                 if (charLength == 2)
  496.                     db.secondByte = WEGetChar(rangeStart + 1, hWE);
  497.  
  498.                 // prepend the character to be deleted to the beginning of our saved text handle
  499.                 if ((err = _WEInsertBlock(pAction->hText, &db, charLength, 0)) != noErr)
  500.                     return err;
  501.  
  502.                 // adjust internal counters
  503.                 pAction->insRangeLength += charLength;
  504.                 pAction->delRangeStart -= charLength;
  505.  
  506.                 // get style run info associated with the about-to-be-deleted character
  507.                 WEGetRunInfo(rangeStart, &runInfo, hWE);
  508.  
  509.                 // prepend a new style element to our style scrap, if necessary
  510.                 if ((err = _WEPrependStyle(pAction->hStyles, &runInfo, charLength)) != noErr)
  511.                     return err; 
  512.  
  513. #if WASTE_OBJECTS
  514.                 // do the same with our object "soup"
  515.                 if ((err = _WEPrependObject(pAction->hSoup, &runInfo, charLength)) != noErr)
  516.                     return err;
  517. #endif
  518.             
  519.             } // if deleting old text
  520.  
  521.             // unlock the action record
  522.             _WESetHandleLock((Handle) pWE->hActionStack, saveActionLock);
  523.  
  524.         } // if undo support is enabled
  525.     } // if selection is empty
  526.  
  527.     if ((err = _WEDeleteRange(rangeStart, rangeEnd, hWE)) != noErr)
  528.         return err;
  529.  
  530.     // keep track of current selection range
  531.     pWE->selStart = rangeStart;
  532.     pWE->selEnd = rangeStart;
  533.  
  534.     // redraw the text
  535.     err = _WERedraw(rangeStart, rangeStart, hWE);
  536.     
  537.     return err;
  538. } // _WEBackspace
  539.  
  540. pascal OSErr _WEForwardDelete(WEHandle hWE)
  541. {
  542.     
  543.     // this routine is called by WEKey to handle the forward delete key
  544.  
  545.     WEPtr pWE = *hWE;    // assume WE record is already locked
  546.     WEActionPtr pAction;
  547.     long rangeStart, rangeEnd, charLength;
  548.     WERunInfo runInfo;
  549.     DoubleByte db;
  550.     Boolean saveActionLock;
  551.     OSErr err;
  552.  
  553.     // calculate the text range to delete
  554.     // if the selection is non-empty, delete that
  555.     rangeStart = pWE->selStart;
  556.     rangeEnd = pWE->selEnd;
  557.     if (rangeStart == rangeEnd)
  558.     {
  559.         
  560.         // otherwise the selection is an insertion point
  561.         // do nothing if insertion point is at the end of the text
  562.         if (rangeStart == pWE->textLength)
  563.             return noErr;
  564.  
  565.         // determine the byte-type of the character following the insertion point
  566.         charLength = (WECharByte(rangeStart, hWE) == smSingleByte) ? 1 : 2;
  567.         rangeEnd = rangeStart + charLength;
  568.  
  569.         if (pWE->hActionStack != NULL)
  570.         {
  571.  
  572.             // UNDO SUPPORT FOR FORWARD DELETE
  573.  
  574.             // lock the action record
  575.             saveActionLock = _WESetHandleLock((Handle) pWE->hActionStack, true);
  576.             pAction = *pWE->hActionStack;
  577.  
  578.             // make a copy of the character about to be deleted
  579.             db.firstByte = WEGetChar(rangeStart, hWE);
  580.             if (charLength == 2)
  581.                 db.secondByte = WEGetChar(rangeStart + 1, hWE);
  582.  
  583.             // append it to the end of our saved text handle
  584.             PtrAndHand(&db, pAction->hText, charLength);
  585.             if ((err = MemError()) != noErr)
  586.                 return err;
  587.  
  588.             // get style run info associated with the about-to-be-deleted character
  589.             WEGetRunInfo(rangeStart, &runInfo, hWE);
  590.  
  591.             // append a new style element to our style scrap, if necessary
  592.             if ((err = _WEAppendStyle(pAction->hStyles, &runInfo, pAction->insRangeLength)) != noErr)
  593.                 return err;
  594.  
  595. #if WASTE_OBJECTS
  596.             // do the same with our object soup
  597.             if ((err = _WEAppendObject(pAction->hSoup, &runInfo, pAction->insRangeLength)) != noErr)
  598.                 return err;
  599. #endif
  600.  
  601.             // adjust internal counters
  602.             pAction->insRangeLength += charLength;
  603.  
  604.             // unlock the action record
  605.             _WESetHandleLock((Handle) pWE->hActionStack, saveActionLock);
  606.  
  607.         } // if undo support is enabled
  608.     } // if selection is empty
  609.  
  610.     if ((err = _WEDeleteRange(rangeStart, rangeEnd, hWE)) != noErr)
  611.         return err;
  612.  
  613.     // keep track of current selection range
  614.     pWE->selStart = rangeStart;
  615.     pWE->selEnd = rangeStart;
  616.  
  617.     // redraw the text
  618.     err = _WERedraw(rangeStart, rangeStart, hWE);
  619.  
  620.     return err;
  621. } // _WEForwardDelete
  622.  
  623. pascal Boolean WEIsTyping(WEHandle hWE)
  624. {
  625.     // return TRUE if we're tracking a typing sequence in the specified WE instance
  626.  
  627.     WEPtr pWE = *hWE;
  628.     WEActionPtr pAction;
  629.  
  630.     // there must be an undo buffer
  631.     if (pWE->hActionStack == NULL)
  632.         return false;
  633.     
  634.     pAction = *pWE->hActionStack;
  635.  
  636.     // the action kind must be "typing" and the redo flag must be clear
  637.     if ((pAction->actionKind != weAKTyping) || ((pAction->actionFlags & weAFIsRedo) != 0))
  638.         return false;
  639.  
  640.     // finally, the selection range mustn't have moved since the last WEKey
  641.     if ((pWE->selStart != pWE->selEnd) ||
  642.         (pWE->selStart != pAction->delRangeStart + pAction->delRangeLength))
  643.         return false;
  644.     
  645.     return true;
  646. } // _WEIsTyping
  647.  
  648. pascal void WEKey(short key, EventModifiers modifiers, WEHandle hWE)
  649. {
  650.     WEPtr pWE;
  651.     WEActionHandle hAction;
  652.     Boolean saveWELock;
  653.     OSErr err;
  654.     
  655.     err = noErr;
  656.  
  657.     // lock the WE record
  658.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  659.     pWE = *hWE;
  660.  
  661.     // hide the caret if it's showing
  662.     if (BTST(pWE->flags, weFCaretVisible))
  663.         _WEBlinkCaret(hWE);
  664.  
  665.     // hide the cursor (it will show again as soon as it's moved)
  666.     ObscureCursor();
  667.  
  668.     // dispatch on key class (arrow keys, printable characters, backspace)
  669.     if ((key >= kArrowLeft) && (key <= kArrowDown)) 
  670.             _WEDoArrowKey(key, modifiers, hWE);
  671.     else
  672.     {
  673.  
  674.         // non-arrow keys modify the text, so make sure editing is allowed
  675.         if (!BTST(pWE->features, weFReadOnly))
  676.         {
  677.             // are we tracking a typing sequence?
  678.             if (WEIsTyping(hWE) == false)
  679.             {
  680.                 // nope;  start a new one
  681.                 // increment modification count
  682.                 pWE->modCount++;
  683.  
  684.                 // if undo support is enabled, create a new action to keep track of typing
  685.                 if (BTST(pWE->features, weFUndoSupport))
  686.                 {
  687.                     WEClearUndo(hWE);
  688.                     if (WENewAction(pWE->selStart, pWE->selEnd, 0, weAKTyping, 0, hWE, &hAction) == noErr) 
  689.                         if (WEPushAction(hAction) != noErr)
  690.                         {
  691.                             ;
  692.                         }
  693.                 }
  694.             } // if WEIsTyping
  695.  
  696.             if (key == kBackspace) 
  697.                 err = _WEBackspace(hWE);
  698.             else if (key == kForwardDelete)
  699.                 err = _WEForwardDelete(hWE);
  700.             else
  701.                 err = _WETypeChar(key, hWE);
  702.         } // if not read-only
  703.         
  704.     }
  705.  
  706.     // unlock the WE record
  707.     _WESetHandleLock((Handle) hWE, saveWELock);
  708.  
  709. } // WEKey
  710.  
  711. pascal OSErr WEInsert(Ptr textPtr, long textLength, StScrpHandle hStyles, Handle hSoup, WEHandle hWE)
  712. {
  713. #if WASTE_OBJECTS
  714. #pragma unused(hSoup)
  715. #endif
  716.     WEPtr pWE;
  717.     long offset, endOffset;
  718.     WEActionHandle hAction;
  719.     short intPasteAction;
  720.     Boolean saveWELock;
  721.     char space;
  722.     OSErr err;
  723.  
  724.     // lock the WE record
  725.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  726.     pWE = *hWE;
  727.     offset = pWE->selStart;
  728.  
  729.     // return an error code if this instance is read-only
  730.     err = weReadOnlyErr;
  731.     if (BTST(pWE->features, weFReadOnly))
  732.         goto cleanup;
  733.  
  734.     // stop any ongoing inline input session
  735.     WEStopInlineSession(hWE);
  736.  
  737.     // increment modification count
  738.     pWE->modCount++;
  739.  
  740.     // if undo support is enabled, save current selection range
  741.     if (BTST(pWE->features, weFUndoSupport)) 
  742.     {
  743.         WEClearUndo(hWE);
  744.         if (WENewAction(offset, pWE->selEnd, textLength, weAKUnspecified, 0, hWE, &hAction) == noErr) 
  745.             if (WEPushAction(hAction) != noErr) 
  746.                 { ;    }
  747.     }
  748.  
  749.     // delete current selection
  750.     if ((err = _WEDeleteRange(offset, pWE->selEnd, hWE)) != noErr) 
  751.         goto cleanup;
  752.  
  753.     // insert the new text at the insertion point
  754.     if ((err = _WEInsertText(offset, textPtr, textLength, hWE)) != noErr) 
  755.         goto cleanup;
  756.     endOffset = offset + textLength;
  757.  
  758.     if (hStyles != NULL) 
  759.     {
  760.  
  761.         // if a style scrap was supplied, apply it to the newly inserted text
  762.         if ((err = _WEApplyStyleScrap(offset, endOffset, hStyles, hWE)) != noErr) 
  763.             goto cleanup;
  764.     }
  765.  
  766. #if WASTE_OBJECTS
  767.     if (hSoup != NULL)
  768.     {
  769.  
  770.         // if an object soup was supplied, apply it to the newly inserted text
  771.         if ((err = _WEApplySoup(offset, hSoup, hWE)) != noErr) 
  772.             goto cleanup;
  773.     }
  774. #endif
  775.  
  776.     // determine whether an extra space should be added before or after the inserted text
  777.     intPasteAction = _WEIntelligentPaste(offset, endOffset, hWE);
  778.  
  779.     // add the extra space, if necessary
  780.     if (intPasteAction != weDontAddSpaces) 
  781.     {
  782.  
  783.         space = kSpace;
  784.         if (intPasteAction == weAddSpaceOnLeftSide) 
  785.             err = _WEInsertText(offset, &space, 1, hWE);
  786.         else
  787.         if ((err = _WEInsertText(endOffset, &space, 1, hWE)) != noErr) 
  788.             goto cleanup;
  789.         endOffset++;
  790.  
  791.         // adjust undo buffer (if any) for the extra space
  792.         _WEAdjustUndoRange(1, hWE);
  793.  
  794.     }
  795.  
  796.     // invalid the null style
  797.     BCLR(pWE->flags, weFUseNullStyle);
  798.  
  799.     // move the insertion point at the end of the inserted text
  800.     pWE->selStart = endOffset;
  801.     pWE->selEnd = endOffset;
  802.  
  803.     // redraw the text
  804.     if ((err = _WERedraw(offset, endOffset, hWE)) != noErr) 
  805.         goto cleanup;
  806.  
  807.     // clear result code
  808.     err = noErr;
  809.  
  810. cleanup:
  811.     // unlock the WE record
  812.     _WESetHandleLock((Handle) hWE, saveWELock);
  813.  
  814.     // return result code
  815.     return err;
  816. } // WEInsert
  817.  
  818. #if WASTE_OBJECTS
  819.  
  820. pascal OSErr WEInsertObject(FlavorType objectType, Handle objectDataHandle, Point objectSize, WEHandle hWE)
  821. {
  822.     WEPtr pWE;
  823.     WEActionHandle hAction;
  824.     long offset, endOffset;
  825.     WETextStyle ts;
  826.     char marker;
  827.     Boolean saveWELock;
  828.     OSErr err;
  829.  
  830.     BLOCK_CLR(ts);
  831.  
  832.     // lock the WE record
  833.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  834.     pWE = *hWE;
  835.     offset = pWE->selStart;
  836.  
  837.     // return an error code if this instance is read-only
  838.     err = weReadOnlyErr;
  839.     if (BTST(pWE->features, weFReadOnly))
  840.         goto cleanup;
  841.  
  842.     // stop any ongoing inline input session
  843.     WEStopInlineSession(hWE);
  844.  
  845.     // call the 'new' handler to initialize private object storage (if any)
  846.     // and to calculate the default size for this object
  847.  
  848.     if ((err = _WENewObject(objectType, objectDataHandle, hWE, &ts.tsObject)) != noErr) 
  849.         goto cleanup;
  850.  
  851.     // use the specified object size, unless it is (0, 0), in which case keep the default size
  852.     if (*((long *) &objectSize) != 0) 
  853.         (*ts.tsObject)->objectSize = objectSize;
  854.  
  855.     // increment modification count
  856.     pWE->modCount++;
  857.  
  858.     // if undo support is enabled, save current selection range
  859.     if (BTST(pWE->features, weFUndoSupport)) 
  860.     {
  861.         WEClearUndo(hWE);
  862.         if (WENewAction(offset, pWE->selEnd, 1, weAKUnspecified, 0, hWE, &hAction) == noErr) 
  863.             if (WEPushAction(hAction) != noErr) 
  864.                 { ; }
  865.     }
  866.  
  867.     // delete current selection
  868.     if ((err = _WEDeleteRange(offset, pWE->selEnd, hWE)) != noErr) 
  869.         goto cleanup;
  870.  
  871.     // insert a kObjectMarker character at the insertion point
  872.     marker = kObjectMarker;
  873.     if ((err = _WEInsertText(offset, &marker, 1, hWE)) != noErr) 
  874.         goto cleanup;
  875.  
  876.     // move the insertion point after the inserted text
  877.     endOffset = offset + 1;
  878.     pWE->selStart = endOffset;
  879.     pWE->selEnd = endOffset;
  880.  
  881.     // record a reference to the object descriptor in the style table
  882.     err = _WESetStyleRange(offset, endOffset, weDoObject, &ts, hWE);
  883.     ts.tsObject = NULL;
  884.     if (err != noErr) 
  885.         goto cleanup;
  886.  
  887.     // invalid the null style
  888.     BCLR(pWE->flags, weFUseNullStyle);
  889.  
  890.     // redraw the text
  891.     if ((err = _WERedraw(offset, endOffset, hWE)) != noErr) 
  892.         goto cleanup;
  893.  
  894.     // clear result code
  895.     err = noErr;
  896.  
  897. cleanup:
  898.     // clean up
  899.     _WEForgetHandle((Handle *) &ts.tsObject);
  900.  
  901.     // unlock the WE record
  902.     _WESetHandleLock((Handle) hWE, saveWELock);
  903.  
  904.     // return result code
  905.     return err;
  906. } // WEInsertObject
  907.  
  908. #endif
  909.  
  910. pascal OSErr WEDelete(WEHandle hWE)
  911. {
  912.     WEPtr pWE;
  913.     WEActionHandle hAction;
  914.     long rangeStart, rangeEnd;
  915.     Boolean saveWELock;
  916.     OSErr err;
  917.  
  918.     // lock the WE record
  919.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  920.     pWE = *hWE;
  921.  
  922.     // return an error code if this instance is read-only
  923.     err = weReadOnlyErr;
  924.     if (BTST(pWE->features, weFReadOnly))
  925.         goto cleanup;
  926.  
  927.     // stop any ongoing inline input session
  928.     WEStopInlineSession(hWE);
  929.  
  930.     // get current selection range
  931.     rangeStart = pWE->selStart;
  932.     rangeEnd = pWE->selEnd;
  933.  
  934.     // do nothing if the selection range is empty
  935.     if (rangeStart < rangeEnd) 
  936.     {
  937.  
  938.         // increment modification count
  939.         pWE->modCount++;
  940.  
  941.         // range extension for intelligent cut-and-paste
  942.         _WEIntelligentCut(&rangeStart, &rangeEnd, hWE);
  943.  
  944.         // if undo support is enabled, save the range to be deleted
  945.         if (BTST(pWE->features, weFUndoSupport)) 
  946.         {
  947.             WEClearUndo(hWE);
  948.             if (WENewAction(rangeStart, rangeEnd, 0, weAKClear, 0, hWE, &hAction) == noErr) 
  949.                 if (WEPushAction(hAction) != noErr) 
  950.                     { ; }
  951.         }
  952.  
  953.         // delete the selection range
  954.         if ((err = _WEDeleteRange(rangeStart, rangeEnd, hWE)) != noErr) 
  955.             goto cleanup;
  956.  
  957.         // reset the selection range
  958.         pWE->selStart = rangeStart;
  959.         pWE->selEnd = rangeStart;
  960.  
  961.         // redraw the text
  962.         if ((err = _WERedraw(rangeStart, rangeStart, hWE)) != noErr) 
  963.             goto cleanup;
  964.  
  965.     } // if non-empty selection
  966.  
  967.     // clear result code
  968.     err = noErr;
  969.  
  970. cleanup:
  971.     // unlock the WE record
  972.     _WESetHandleLock((Handle) hWE, saveWELock);
  973.  
  974.     // return result code
  975.     return err;
  976. } // WEDelete
  977.  
  978. pascal OSErr WECut(WEHandle hWE)
  979. {
  980.     OSErr err;
  981.  
  982.     // first copy...
  983.     if ((err = WECopy(hWE)) != noErr)
  984.         return err;
  985.  
  986.     // ... then delete
  987.     if ((err = WEDelete(hWE)) != noErr)
  988.         return err;
  989.  
  990.     // change the action kind of the most recent action, if any
  991.     if ((*hWE)->hActionStack != NULL) 
  992.         (*(*hWE)->hActionStack)->actionKind = weAKCut;
  993.  
  994.     return noErr;
  995. }  // WECut
  996.  
  997. pascal Boolean WECanPaste(WEHandle hWE)
  998. {
  999.     long scrapOffset;
  1000. #if WASTE_OBJECTS
  1001.     FlavorType objectType;
  1002.     long index;
  1003. #endif
  1004.  
  1005.     if (!BTST((*hWE)->features, weFReadOnly))
  1006.     {
  1007.         // return TRUE if the desk scrap contains a text flavor
  1008.         if (GetScrap(NULL, kTypeText, &scrapOffset) > 0)
  1009.             return true;
  1010.  
  1011. #if WASTE_OBJECTS
  1012.         // see if the desk scrap contains a flavor matching one of the registered object types
  1013.         index = 0;
  1014.         while (_WEGetIndObjectType(index, &objectType, hWE) == noErr)
  1015.         {
  1016.             if (GetScrap(NULL, objectType, &scrapOffset) > 0)
  1017.                 return true;
  1018.             index++;
  1019.         } // while
  1020. #endif
  1021.     } // if not read-only
  1022.     return false;
  1023. } // WECanPaste
  1024.  
  1025. pascal OSErr WEPaste(WEHandle hWE)
  1026. {
  1027.     Handle hItem = NULL;
  1028.     Handle hStyles = NULL;
  1029.     Handle hSoup = NULL;
  1030.     long selStart = (*hWE)->selStart;
  1031.     long scrapOffset;
  1032.     OSErr err;
  1033. #if WASTE_OBJECTS
  1034.     FlavorType objectType;
  1035.     long index;
  1036. #endif
  1037.  
  1038.     // allocate a handle to hold a scrap item
  1039.     if ((err = _WEAllocate(0, kAllocTemp, &hItem)) != noErr) 
  1040.         goto cleanup;
  1041.  
  1042.     // look for a text flavor
  1043.     if (GetScrap(hItem, kTypeText, &scrapOffset) <= 0) 
  1044.     {
  1045.  
  1046. #if WASTE_OBJECTS
  1047.         // no text: look for a flavor matching one of the registered object types
  1048.         index = 0;
  1049.         while (_WEGetIndObjectType(index, &objectType, hWE) == noErr)
  1050.         {
  1051.             if (GetScrap(hItem, objectType, &scrapOffset) > 0) 
  1052.             {
  1053.                 Point objectSize;
  1054.                 objectSize.v = 0;
  1055.                 objectSize.h = 0;
  1056.             
  1057.                 // found a registered type: create a new object out of the tagged data
  1058.                 err = WEInsertObject(objectType, hItem, objectSize, hWE);
  1059.  
  1060.                 // if successful, set hItem to NULL so clean-up section won't kill the object data
  1061.                 if (err == noErr) 
  1062.                     hItem = NULL;
  1063.                 goto cleanup;
  1064.             }
  1065.  
  1066.             // try with next flavor
  1067.             index++;
  1068.         } // while
  1069. #endif
  1070.  
  1071.         // nothing pasteable: return an error code
  1072.         err = noTypeErr;
  1073.         goto cleanup;
  1074.     }
  1075.  
  1076.     // allocate a handle to hold the style scrap, if any
  1077.     if ((err = _WEAllocate(0, kAllocTemp, &hStyles)) != noErr) 
  1078.         goto cleanup;
  1079.  
  1080.     // look for a 'styl' item accompanying the text
  1081.     if (GetScrap(hStyles, kTypeStyles, &scrapOffset) <= 0) 
  1082.         // forget the handle if nothing was found or an error occurred
  1083.         _WEForgetHandle(&hStyles);
  1084.  
  1085. #if WASTE_OBJECTS
  1086.     // allocate a handle to hold the soup, if any
  1087.     if ((err = _WEAllocate(0, kAllocTemp, &hSoup)) != noErr) 
  1088.         goto cleanup;
  1089.  
  1090.     // look for a 'SOUP' item accompanying the text
  1091.     if (GetScrap(hSoup, kTypeSoup, &scrapOffset) <= 0) 
  1092.         // forget the handle if nothing was found or an error occurred
  1093.         _WEForgetHandle(&hSoup);
  1094. #endif
  1095.  
  1096.     // lock down the text
  1097.     HLock(hItem);
  1098.  
  1099.     // insert the text
  1100.     err = WEInsert(*hItem, GetHandleSize(hItem), (StScrpHandle) hStyles, hSoup, hWE);
  1101.  
  1102. cleanup:
  1103.     // if successful, change the action kind of the most recent action, if any
  1104.     if (err == noErr) 
  1105.         if ((*hWE)->hActionStack != NULL) 
  1106.             (*(*hWE)->hActionStack)->actionKind = weAKPaste;
  1107.  
  1108.     // clean up
  1109.     _WEForgetHandle(&hItem);
  1110.     _WEForgetHandle(&hStyles);
  1111. #if WASTE_OBJECTS
  1112.     _WEForgetHandle(&hSoup);
  1113. #endif
  1114.  
  1115.     // return result code
  1116.     return err;
  1117. } // WEPaste
  1118.  
  1119. pascal OSErr _WESmartSetFont(short mode, const TextStyle *ts, WEHandle hWE)
  1120. {
  1121.     WEPtr pWE = *hWE;    // assume WE record is already locked
  1122.     ScriptCode script;
  1123.     long runIndex;
  1124.     long rangeStart, rangeEnd;
  1125.     WERunInfo runInfo;
  1126.     GrafPtr savePort;
  1127.     short saveFont;
  1128.     OSErr err;
  1129.     
  1130.     // set up the graphics port
  1131.     GetPort(&savePort);
  1132.     SetPort(pWE->port);
  1133.     saveFont = pWE->port->txFont;
  1134.  
  1135.     // get the script corresponding to the font we're applying
  1136.     script = FontToScript(ts->tsFont);
  1137.  
  1138.     // walk through the style runs encompassing the selection range
  1139.     runIndex = _WEOffsetToRun(pWE->selStart, hWE);
  1140.     do {
  1141.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  1142.         
  1143.         if (pWE->selStart > runInfo.runStart)
  1144.             rangeStart = pWE->selStart;
  1145.         else
  1146.             rangeStart = runInfo.runStart;
  1147.         
  1148.         if (pWE->selEnd < runInfo.runEnd)
  1149.             rangeEnd = pWE->selEnd;
  1150.         else
  1151.             rangeEnd = runInfo.runEnd;
  1152.  
  1153.         // does this style run belong to the same script we're applying?
  1154.         if (FontToScript(runInfo.runAttrs.runStyle.tsFont) == script)
  1155.         {
  1156.             if ((err = _WESetStyleRange(rangeStart, rangeEnd, weDoFont, (WETextStyle *) ts, hWE)) != noErr)
  1157.                 goto cleanup;
  1158.             runIndex = _WEOffsetToRun(runInfo.runEnd, hWE);
  1159.         }
  1160.         else if ((mode & weDoExtractSubscript) != 0)
  1161.         {
  1162.             long runLength;
  1163.             long subrunLength;
  1164.             ScriptRunStatus runStatus;
  1165.             
  1166.             // FindScriptRun takes an implicit parameter through the txFont field of thePort
  1167.             TextFont(runInfo.runAttrs.runStyle.tsFont);
  1168.             
  1169.             runLength = rangeEnd - rangeStart;
  1170.             while (runLength > 0)
  1171.             {
  1172.                 // lock text handle
  1173.                 Boolean saveTextLock = _WESetHandleLock(pWE->hText, true);
  1174.     
  1175.                 // look for blocks of subscript text
  1176.                 runStatus = FindScriptRun(*pWE->hText + rangeStart, runLength, &subrunLength);
  1177.     
  1178.                 // unlock text handle
  1179.                 _WESetHandleLock(pWE->hText, saveTextLock);
  1180.     
  1181.                 if (runStatus.script == script)
  1182.                 {
  1183.                     // "extract" subscript text
  1184.                     if ((err = _WESetStyleRange(rangeStart, rangeStart + subrunLength, weDoFont, (WETextStyle *) ts, hWE)) != noErr)
  1185.                         goto cleanup;
  1186.                 }
  1187.                 rangeStart += subrunLength;
  1188.                 runLength -= subrunLength;
  1189.             }
  1190.             runIndex = _WEOffsetToRun(runInfo.runEnd, hWE);
  1191.         }
  1192.         else
  1193.             runIndex++;
  1194.  
  1195.     } while (runInfo.runEnd < pWE->selEnd);
  1196.  
  1197. cleanup:
  1198.     // restore the port
  1199.     TextFont(saveFont);
  1200.     SetPort(savePort);
  1201.     
  1202.     // return result code
  1203.     return err;
  1204. }
  1205.  
  1206. pascal OSErr WESetStyle(short mode, const TextStyle *ts, WEHandle hWE)
  1207. {
  1208.     WEPtr pWE;
  1209.     WEActionHandle hAction;
  1210.     ScriptCode fontScript;
  1211.     Boolean saveWELock;
  1212.     OSErr err;
  1213.  
  1214.     // lock the WE record
  1215.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  1216.     pWE = *hWE;
  1217.  
  1218.     // return an error code if this instance is read-only
  1219.     err = weReadOnlyErr;
  1220.     if (BTST(pWE->features, weFReadOnly))
  1221.         goto cleanup;
  1222.  
  1223.     // stop any ongoing inline input session
  1224.     WEStopInlineSession(hWE);
  1225.  
  1226.     if (pWE->selStart == pWE->selEnd) 
  1227.     {
  1228.  
  1229.         // NULL SELECTION
  1230.         // first make sure the nullStyle field contains valid information
  1231.         _WESynchNullStyle(hWE);
  1232.  
  1233.         // apply style changes to the nullStyle record
  1234.         _WECopyStyle((WETextStyle *)ts, &pWE->nullStyle.runStyle, pWE->nullStyle.runStyle.tsFace, mode);
  1235.  
  1236.         // if the font was altered, synchronize the keyboard script
  1237.         if (BTST(pWE->flags, weFNonRoman)) 
  1238.             if (mode & weDoFont) 
  1239.             {
  1240.                 fontScript = FontToScript(pWE->nullStyle.runStyle.tsFont);
  1241.                 if (fontScript != GetScriptManagerVariable(smKeyScript)) 
  1242.                     KeyScript(fontScript);
  1243.             }
  1244.     }
  1245.     else
  1246.     {
  1247.         // NON-EMPTY SELECTION
  1248.  
  1249.         // increment modification count
  1250.         pWE->modCount++;
  1251.  
  1252.         // if undo support is enabled, save the styles of the text range to be affected
  1253.         if (BTST(pWE->features, weFUndoSupport)) 
  1254.         {
  1255.             WEClearUndo(hWE);
  1256.             if (WENewAction(pWE->selStart, pWE->selEnd, pWE->selEnd - pWE->selStart, weAKSetStyle,
  1257.                 weAFDontSaveText + weAFDontSaveSoup, hWE, &hAction) == noErr) 
  1258.                 if (WEPushAction(hAction) != noErr) 
  1259.                     { ; }
  1260.         }
  1261.  
  1262.         // check for "smart" font modes
  1263.         if (BTST(pWE->flags, weFNonRoman) && ((mode & weDoSmartFont) == weDoSmartFont))
  1264.         {
  1265.             if ((err = _WESmartSetFont(mode, ts, hWE)) != noErr)
  1266.                 goto cleanup;
  1267.             mode &= ~weDoFont;
  1268.         }
  1269.  
  1270.         // set the style of the selection range
  1271.         if ((err = _WESetStyleRange(pWE->selStart, pWE->selEnd, mode, (WETextStyle *)ts, hWE)) != noErr) 
  1272.             goto cleanup;
  1273.  
  1274.         // and redraw the text
  1275.         if ((err = _WERedraw(pWE->selStart, pWE->selEnd, hWE)) != noErr) 
  1276.             goto cleanup;
  1277.     }
  1278.  
  1279.     // clear the result code
  1280.     err = noErr;
  1281.  
  1282. cleanup:
  1283.     // unlock the WE record
  1284.     _WESetHandleLock((Handle) hWE, saveWELock);
  1285.  
  1286.     // return result code
  1287.     return err;
  1288. } // WESetStyle
  1289.  
  1290. pascal OSErr WEUseStyleScrap(StScrpHandle hStyles, WEHandle hWE)
  1291. {
  1292.     WEPtr pWE;
  1293.     Boolean saveWELock;
  1294.     OSErr err;
  1295.  
  1296.     // lock the WE record
  1297.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  1298.     pWE = *hWE;
  1299.  
  1300.     // return an error code if this instance is read-only
  1301.     err = weReadOnlyErr;
  1302.     if (BTST(pWE->features, weFReadOnly))
  1303.         goto cleanup;
  1304.  
  1305.     // apply the style scrap to the selection range
  1306.     if ((err = _WEApplyStyleScrap(pWE->selStart, pWE->selEnd, hStyles, hWE)) != noErr) 
  1307.         goto cleanup;
  1308.  
  1309.     // redraw the text
  1310.     err = _WERedraw(pWE->selStart, pWE->selEnd, hWE);
  1311.  
  1312. cleanup:
  1313.     // unlock the WE record
  1314.     _WESetHandleLock((Handle) hWE, saveWELock);
  1315.  
  1316.     // return result code
  1317.     return err;
  1318. }
  1319.